package com.lognsoft.btest;

import java.io.File;
import java.util.ArrayList;
import java.lang.Exception;
import java.io.FileWriter;
import java.util.Properties;
import java.io.IOException;
import com.lognsoft.shell.*;

/*
 * set-up:
 * 
 * tear-down:
 * 
 * main:
 * 		test-cases=$(SYSTEM-OS)
 * 
 * 
 * 
 * 
 * */

class XMLOutputStream extends StringLineOutputStream
{
	
};

class ResultOutputStream extends StringLineOutputStream
{
	FileWriter writer;

public ResultOutputStream (FileWriter fw)
{
	writer = fw;
}

public void onRecvLine (String s)
{
	int len = s.length();
			
	if (len > 80)
	{
		for ( int from = 0; from < len; )
		{
			int v_end = from + 70 < len ? from + 70 : len;
			int s_end = v_end;
			
			for ( int i = from; i < v_end ; i++ )
			{
				char ch = s.charAt(i);
				
				if ( ch >='a' && ch <= 'z') continue;
				if ( ch >='A' && ch <= 'Z') continue;
				if ( ch =='-' || ch=='_' || ch=='.') continue;
				
				s_end = i + 1;
			}
			
			onRecvLine ( s.substring(from, s_end));
			from = s_end;
		}		
		return;
	}
	
	try
	{
		if (s.startsWith(TestCaseDef.BTestPrefix))
		{
			writer.write( s.substring(0, TestCaseDef.BTestPrefix.length() - 1));
			writer.write( "\r\n");
		
			writer.write( s.substring(TestCaseDef.BTestPrefix.length() - 1));
			writer.write( "\r\n");
		}
		else
		{
			writer.write(s);
			writer.write( "\r\n");
		}
	}
	catch(Exception e)
	{
		e.printStackTrace();
	}
}

public void writeLines(ArrayList<String> lines)
{
	if (lines == null)
	{
		return;
	}
	
	for (int i = 0; i < lines.size(); i++)
	{
		onRecvLine ( lines.get(i));
	}
}
	
}

public class ExecuteEngine extends TestCaseFile
{
	public static boolean is_baseline = false;
	public static ReportGen report_gen = null;
	
	public ExecuteEngine (String s) throws Exception
	{		
		load(TestCaseDef.Default_TestCase);
		load(s);
	}
	
	public static ArrayList<ExecuteEngine> getAll(
			String fileName, int max_count) throws Exception 
	{
		ArrayList<ExecuteEngine> r = new ArrayList<ExecuteEngine>();
		ExecuteEngine tc;
		
		tc = new ExecuteEngine(fileName);
		
		for (int i = 0; i < r.size(); i++) 
		{
			tc = r.get(i);

			r.addAll(tc.getSons());

			if (r.size() > max_count && max_count > 0) 
			{
				break;
			}
		}

		return r;
	}

	public ArrayList<ExecuteEngine> getSons () throws Exception {
		ArrayList<ExecuteEngine> r = new ArrayList<ExecuteEngine>();

		String val = get_value_of( TestCaseDef.TestSuiteKey);
		String[] SuiteList = val == null || val.length() == 0 
				? null 
				: val.split(" ");
		
	
		if (SuiteList == null) 
		{
			return r;
		}

		for (String i : SuiteList) 
		{
			i = i.trim();
			
			if (i.isEmpty() || i.charAt(0) == '.')
			{
				continue;
			}
			
			i = i + File.separator + TestCaseDef.TestCaseFileName;
			StringOp.KeyValue fn = FileOp.path_cat(base_path, i, true);
	
			if (fn == null) 
			{
				throw new TestCaseException (
						"File not found " + FileOp.relative_path(i));
			}

			ExecuteEngine tc = new ExecuteEngine(fn.key + fn.value);
			
			r.add(tc);
		}

		return r;
	}

	public boolean runBlock (String block_name, String arg) throws Exception
	{
		TestCaseBlock block = push_block(block_name, arg);
		
		if (block == null)
		{
			return false;
		}
	
		System.out.println(
			"--execute " + block.block_name + 
			" at " + FileOp.relative_path(base_path) );
	
		for (String s : block)
		{
			s = calc_expression (s);
			
			ShellExecute sh = new ShellExecute();
			sh.run_stdout(base_path, s, false);
		}
			
		pop_block();
		
		return true;		
	}
	
	public static void write_line (FileWriter fw, String s) throws Exception
	{
		int len = s.length();
		
		if (len > 80)
		{
			for ( int i = 0; i < len; i += 70)
			{
				int end = i + 70;
				
				write_line ( 
					fw,
					s.substring(i, end < len ? end : len) );				
			}
			
			return;
		}
		
		if (s.startsWith(TestCaseDef.BTestPrefix))
		{
			write_line ( 
					fw, s.substring(0, TestCaseDef.BTestPrefix.length() - 1));
			
			write_line (
					fw, s.substring(TestCaseDef.BTestPrefix.length() - 1));
			
			return;
		}
		
		fw.write(s);
		fw.write("\r\n");
	}
	
	public boolean cmd_stdio_test(Properties args, FileWriter fw) throws Exception
	{		
		String testCommand = args.getProperty(TestCaseDef.Test_Command_Key,"");
		//String testOptions = args.getProperty(TestCaseDef.Test_Options_Key,"");
		String path        = args.getProperty(TestCaseDef.PathKey,"");
		
		if (path.isEmpty())
		{
			path = base_path;
		}
		else
		{
			path = FileOp.path_cat_as_string(base_path, path);
		}
		
		fw.write (TestCaseDef.BTestComment + " This is BTest result file version "+Version.VersionString+"\r\n");
		fw.write (TestCaseDef.BTestComment + " outputs from stdout\r\n");

		ResultOutputStream ResultOS = new ResultOutputStream(fw);
		StringLineOutputStream errOS = new StringLineOutputStream();
		
		ShellExecute sh = new ShellExecute();
				
		if ( false == sh.run_fileOut(path, testCommand, errOS, ResultOS) )
		{
			return false;
		}
		
		fw.write (TestCaseDef.BTestComment + " outputs from stderr\r\n");

		ResultOS.writeLines(errOS.getLines());
		
		return true;
	};
	
	public boolean runTestCase (String s) throws Exception
	{
		TestCaseBlock block = push_block(s.toLowerCase(), s);
	
		if (block == null)
		{
			int ind = s.lastIndexOf('.');
			
			if (ind < 0 || ind == (s.length() - 1) )
			{
				return false;
			}			
		
			block = push_block( s.substring(ind).toLowerCase() , s);
			
			if (block == null)
			{
				return false;
			}
		}
		
		String[] case_args = 
		{
			TestCaseDef.AnswerKey,
			TestCaseDef.Output_Files_Key,
			TestCaseDef.Test_Command_Key,
			TestCaseDef.Test_Options_Key,
			TestCaseDef.Test_Type_Key,
			TestCaseDef.PathKey	
		};
		
		Properties prop = get_value_of (case_args);
		
		System.out.println("Case: " + s);
		for (String propKey : case_args )
		{
			System.out.println("     " + propKey + " = " + prop.getProperty(propKey, ""));
		}
		
		String Test_Type = prop.getProperty(TestCaseDef.Test_Type_Key,"").toLowerCase();
		
		String answerFileName = prop.getProperty(TestCaseDef.AnswerKey,"");
		answerFileName = FileOp.path_cat_as_string(base_path, answerFileName);
		String resultFileName = base_path + ".result" + File.separator + s + ".result";
		
		FileWriter fw;		

		try
		{	
			String outFileName = is_baseline ? answerFileName : resultFileName;
			FileOp.createPathOfFile (outFileName);
			fw = new FileWriter (outFileName);			
		}
		catch (IOException e)
		{
			pop_block();
			return false;
		}
		
		boolean ret = true;
		
		if (Test_Type.equals("stdio-test"))
		{
			ret = cmd_stdio_test(prop, fw);
			
			fw.close();			
			
			if (ret)
			{
				ret = ResultCompare.Compare(resultFileName, answerFileName);
			}
		}
		else if (Test_Type.equals("sql-test"))
		{
			fw.close();
			
			ret = false;
		}
		else if (Test_Type.equals("http-test"))
		{
			fw.close();
			
			ret = false;
		}
		else
		{
			fw.write( Test_Type + " type unknown" );
			fw.close();
			
			ret = false;
		}		
	
		pop_block();		
	
		return ret;
	}
	
	public boolean runClean() throws Exception
	{
		ArrayList<ExecuteEngine> TestSuites = getSons ();
		
		File fc = new File(base_path + ".result");
		boolean fc_exists = fc.exists();		
		boolean remove_ret = FileOp.removePath( fc );
		
		if (fc_exists)
		{
			System.out.println (
				"removing " 
				+ FileOp.relative_path( fc.getAbsolutePath()) 
				+ " " 
				+ (remove_ret ? "Success" : "Failed") );
		}
		
		if (TestSuites == null)
		{
			return false;
		}
		
		for (int i = 0; i < TestSuites.size(); i++)
		{
			TestSuites.get(i).runClean();
		}
		
		return true;
	}
	
	public boolean runTestSuite() throws Exception
	{	
		File resultFolder = null;
		
		runBlock(TestCaseDef.SetupBlock, null);
		
		ArrayList<ExecuteEngine> TestSuites = getSons ();
		String[] TestCases = getTestCases(null);
		
		if ( false == is_baseline 
			&& TestCases != null 
			&& TestCases.length > 0 )
		{
			resultFolder = new File(base_path + ".result");
		
			if (false == resultFolder.exists())
			{
				if ( false == resultFolder.mkdirs() )
				{
					System.err.println(
						"Error : Create " + base_path + ".result fauled" 
					);
					return false;
				}
			}
		}

		if (TestCases != null)
		{
			for (String s : TestCases)
			{
				boolean ret = runTestCase(s);
				
				if (is_baseline == false)
				{
					report_gen.addResult (base_path, s, ret);
					System.out.println ( 
						"Run TestCase(" + s + ") " + (ret ? "Success" : "Failed")  
					);
				}
			}
		}
		
		if (TestSuites != null)
		{
			for (ExecuteEngine tc : TestSuites)
			{
				tc.runTestSuite();
			}
		}
		
		runBlock(TestCaseDef.TearDownBlock, null);		
	
		System.out.println("");
		
		return true;
	}
}
